package com.health.central_authentication;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.health.security.CentralAuthenticationToken;
import com.health.security.Student;
import com.health.security.StudentService;
import com.health.util.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.json.JacksonJsonParser;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.WebAttributes;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

/**
 * @author: zipeng Li
 * 2021/6/14  16:42
 */
@Slf4j
@RestController
public class CentralAuthenticationController {
    private static final String appid = "javaee";
    private static final String appSecret = "b3b52e43ccfd";
    private static final RestTemplate rest = new RestTemplate();
    private static final JacksonJsonParser jsonParser = new JacksonJsonParser();
    private static final String GetAccessTokenURL = "https://cas.dgut.edu.cn/ssoapi/v2/checkToken";
    private static final String GetInfoURL = "https://cas.dgut.edu.cn/oauth/getUserInfo";

    @Resource
    private StudentService studentService;

    @GetMapping("/api/central/login")
    public void login(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 重定向到中央认证
        response.sendRedirect("https://cas.dgut.edu.cn?appid=" + appid);
    }

    @GetMapping("/login/dgut")
    public void callBack(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 新建未认证的 authentication
        String token = request.getParameter("token");
        CentralAuthenticationToken centralAuthenticationToken = new CentralAuthenticationToken(token);
        try {
            centralAuthenticationToken = authenticate(centralAuthenticationToken);
        } catch (AuthenticationException e) {
            // 如果校验失败providerManager会抛异常，在catch里作异常处理。
            response.sendRedirect("https://cas.dgut.edu.cn?appid=" + appid);
            return;
        }
        // 校验成功，执行后面的流程：
        SecurityContextHolder.getContext().setAuthentication(centralAuthenticationToken);
        // 移除之前认证时的错误信息
        request.getSession().removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);

        // 生产token返回
        JwtUtils jwtUtils = new JwtUtils();
        String res = jwtUtils.generateTokenByCentralAuthenticationToken(centralAuthenticationToken);
        log.info(res);
        response.sendRedirect("http://localhost:8081?token=" + res);
    }

    private CentralAuthenticationToken authenticate(Authentication authentication) throws AuthenticationException {
        // 1.强转 未认证的CentralAuthenticationToken
        CentralAuthenticationToken authenticationToken = (CentralAuthenticationToken) authentication;
        // 2.向中央认证服务器发送请求获取 openid 和 access_token
        LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
        map.add("token", authenticationToken.getToken());
        map.add("appid", appid);
        map.add("appsecret", appSecret);
        Map<String, Object> stringObjectMap = jsonParser.parseMap(rest.postForObject(GetAccessTokenURL, map, String.class));
        String accessToken = (String) stringObjectMap.get("access_token");
        String openid = (String) stringObjectMap.get("openid");
        // 3.向中央认证服务器发送请求获取个人信息
        map.clear();
        map.add("access_token", accessToken);
        map.add("openid", openid);
        Map<String, Object> objectMap = jsonParser.parseMap(rest.postForObject(GetInfoURL, map, String.class));
        // 4.认证成功，重新生产 authentication
        Student student = new Student();
        student.setUsername((String)objectMap.get("username"));
        student.setName((String)objectMap.get("name"));
        student.setWxOpenid((String)objectMap.get("wx_openid"));
        student.setFacultyTitle((String)objectMap.get("faculty_title"));
        student.setOpenid((String)objectMap.get("openid"));
        // 5. 判断数据库是否已经存在该用户
        QueryWrapper<Student> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(Student::getUsername, student.getUsername());
        studentService.saveOrUpdate(student, wrapper);

        return new CentralAuthenticationToken(student, AuthorityUtils.createAuthorityList("USER"));
    }
}
